package com.gitee.hermer.boot.jee.commons.io;

import java.io.Closeable;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.util.Collection;

import org.springframework.util.FastByteArrayOutputStream;

import com.gitee.hermer.boot.jee.commons.constants.EncodingConstant;
import com.gitee.hermer.boot.jee.commons.utils.StringUtils;



public class IOUtils {

	public final static int IO_BUFFER_SIZE = 32;
	/** 默认缓存大小 */
	public static final int DEFAULT_BUFFER_SIZE = 1024;
	/** 数据流末尾 */
	public static final int EOF = -1;

	
	// -------------------------------------------------------------------------------------- Copy start
		/**
		 * 将Reader中的内容复制到Writer中 使用默认缓存大小
		 * @param reader Reader
		 * @param writer Writer
		 * @return 拷贝的字节数
		 * @throws IOException
		 */
		public static long copy(Reader reader, Writer writer) throws IOException {
			return copy(reader, writer, DEFAULT_BUFFER_SIZE);
		}

		/**
		 * 将Reader中的内容复制到Writer中
		 * 
		 * @param reader Reader
		 * @param writer Writer
		 * @param bufferSize 缓存大小
		 * @return 传输的byte数
		 * @throws IOException
		 */
		public static long copy(Reader reader, Writer writer, int bufferSize) throws IOException {
			return copy(reader, writer, bufferSize, null);
		}

		/**
		 * 将Reader中的内容复制到Writer中
		 * 
		 * @param reader Reader
		 * @param writer Writer
		 * @param bufferSize 缓存大小
		 * @return 传输的byte数
		 * @throws IOException
		 */
		public static long copy(Reader reader, Writer writer, int bufferSize, StreamProgress streamProgress) throws IOException {
			char[] buffer = new char[bufferSize];
			long size = 0;
			int readSize;
			if (null != streamProgress) {
				streamProgress.start();
			}
			while ((readSize = reader.read(buffer, 0, bufferSize)) != EOF) {
				writer.write(buffer, 0, readSize);
				size += readSize;
				writer.flush();
				if (null != streamProgress) {
					streamProgress.progress(size);
				}
			}
			if (null != streamProgress) {
				streamProgress.finish();
			}
			return size;
		}

		/**
		 * 拷贝流，使用默认Buffer大小
		 * 
		 * @param in 输入流
		 * @param out 输出流
		 * @return 传输的byte数
		 * @throws IOException
		 */
		public static long copy(InputStream in, OutputStream out) throws IOException {
			return copy(in, out, DEFAULT_BUFFER_SIZE);
		}

		/**
		 * 拷贝流
		 * 
		 * @param in 输入流
		 * @param out 输出流
		 * @param bufferSize 缓存大小
		 * @return 传输的byte数
		 * @throws IOException
		 */
		public static long copy(InputStream in, OutputStream out, int bufferSize) throws IOException {
			return copy(in, out, bufferSize, null);
		}

		/**
		 * 拷贝流
		 * 
		 * @param in 输入流
		 * @param out 输出流
		 * @param bufferSize 缓存大小
		 * @param streamProgress 进度条
		 * @return 传输的byte数
		 * @throws IOException
		 */
		public static long copy(InputStream in, OutputStream out, int bufferSize, StreamProgress streamProgress) throws IOException {
			if (null == in) {
				throw new NullPointerException("InputStream is null!");
			}
			if (null == out) {
				throw new NullPointerException("OutputStream is null!");
			}
			if (bufferSize <= 0) {
				bufferSize = DEFAULT_BUFFER_SIZE;
			}

			byte[] buffer = new byte[bufferSize];
			long size = 0;
			if (null != streamProgress) {
				streamProgress.start();
			}
			for (int readSize = -1; (readSize = in.read(buffer)) != EOF;) {
				out.write(buffer, 0, readSize);
				size += readSize;
				out.flush();
				if (null != streamProgress) {
					streamProgress.progress(size);
				}
			}
			if (null != streamProgress) {
				streamProgress.finish();
			}
			return size;
		}

		/**
		 * 拷贝文件流，使用NIO
		 * 
		 * @param in 输入
		 * @param out 输出
		 * @return 拷贝的字节数
		 * @throws IOException
		 */
		public static long copy(FileInputStream in, FileOutputStream out) throws IOException {
			if (null == in) {
				throw new NullPointerException("FileInputStream is null!");
			}
			if (null == out) {
				throw new NullPointerException("FileOutputStream is null!");
			}

			FileChannel inChannel = in.getChannel();
			FileChannel outChannel = out.getChannel();

			return inChannel.transferTo(0, inChannel.size(), outChannel);
		}
		// -------------------------------------------------------------------------------------- Copy end
		
	/**
	 * Convert inputStream to String
	 * @params encoding 可以为"", 默认为 GBK
	 * @throws IOException
	 */
	public static String convertInputStream2String(InputStream is, String encoding) throws IOException {
		if (null == is)
			return "";
		if (StringUtils.isBlank(encoding)) {
			encoding = EncodingConstant.GBK;
		}
		BufferedReader in = new BufferedReader(new InputStreamReader(is, encoding));
		StringBuffer buffer = new StringBuffer();
		String line = "";
		while ((line = in.readLine()) != null) {
			buffer.append(line).append("\n");
		}
		return buffer.toString();
	}

	/**
	 * Convert String to InputStream with special encoding
	 * @param str 字符串
	 * @param encoding 编码
	 * @return InputStream 流
	 * @throws UnsupportedEncodingException
	 */
	public static InputStream convertString2InputStream(String str, String encoding) throws UnsupportedEncodingException {
		if (!StringUtils.isBlank(str)) {
			ByteArrayInputStream stringInputStream = new ByteArrayInputStream(str.getBytes(encoding));
			return stringInputStream;
		}
		return null;
	}

	/** 关闭 InputStream 流 */
	public static void closeInputStream(InputStream is) {
		if (null != is) {
			try {
				is.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/** 关闭 Reader 流 */
	public static void closeReader(Reader reader) {
		if (null != reader) {
			try {
				reader.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/** 关闭 Writer 流 */
	public static void closeWriter(Writer writer) {
		if (null != writer) {
			try {
				writer.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 从流中读取内容
	 * @param in 输入流
	 * @param charset 字符集
	 * @return 内容
	 * @throws IOException
	 */
	public static String getStringFromStream(InputStream in, String charset) throws IOException {
		StringBuilder content = new StringBuilder(); // 存储返回的内容

		// 从返回的内容中读取所需内容
		BufferedReader reader = new BufferedReader(new InputStreamReader(in, charset));
		String line = null;
		while ((line = reader.readLine()) != null) {
			content.append(line);
		}

		return content.toString();
	}

	/**
	 * <p>关闭流(不抛异常)</p>
	 * @param closeable
	 */
	public static void closeQuietly(Closeable closeable) {
		try {
			if (closeable != null) {
				closeable.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * <p>关闭流</p>
	 * @param closeable
	 * @throws IOException
	 */
	public static void close(Closeable closeable) throws IOException {
		if (closeable != null) {
			closeable.close();
		}
	}

	/**
	 * 获得一个文件读取器
	 * 
	 * @param in 输入流
	 * @param charsetName 字符集名称
	 * @return BufferedReader对象
	 * @throws IOException
	 */
	public static BufferedReader getReader(InputStream in, String charsetName) throws IOException {
		return getReader(in, Charset.forName(charsetName));
	}

	/**
	 * 获得一个文件读取器
	 * 
	 * @param in 输入流
	 * @param charset 字符集
	 * @return BufferedReader对象
	 * @throws IOException
	 */
	public static BufferedReader getReader(InputStream in, Charset charset) throws IOException {
		if (null == in) {
			return null;
		}

		InputStreamReader reader = null;
		if (null == charset) {
			reader = new InputStreamReader(in);
		} else {
			reader = new InputStreamReader(in, charset);
		}

		return new BufferedReader(reader);
	}

	/**
	 * 从流中读取bytes
	 * 
	 * @param in 输入流
	 * @return bytes
	 * @throws IOException
	 */
	public static byte[] readBytes(InputStream in) throws IOException {
		final FastByteArrayOutputStream out = new FastByteArrayOutputStream();
		copy(in, out);
		return out.toByteArray();
	}

	/**
	 * 从流中读取内容
	 * @param in 输入流
	 * @param charsetName 字符集
	 * @return 内容
	 * @throws IOException
	 */
	public static String read(InputStream in, String charsetName) throws IOException {
		FastByteArrayOutputStream out = read(in);
		return StringUtils.isBlank(charsetName) ? out.toString() : out.toString();
	}

	/**
	 * 从流中读取内容
	 * @param in 输入流
	 * @param charset 字符集
	 * @return 内容
	 * @throws IOException
	 */
	public static String read(InputStream in, Charset charset) throws IOException {
		FastByteArrayOutputStream out = read(in);
		return null == charset ? out.toString() : out.toString();
	}
	
	/**
	 * 从流中读取内容，读到输出流中
	 * 
	 * @param in 输入流
	 * @return 输出流
	 * @throws IOException
	 */
	public static FastByteArrayOutputStream read(InputStream in) throws IOException {
		final FastByteArrayOutputStream out = new FastByteArrayOutputStream();
		copy(in, out);
		return out;
	}
	
	/**
	 * 从Reader中读取String
	 * @param reader Reader
	 * @return String
	 * @throws IOException
	 */
	public static String read(Reader reader) throws IOException {
		final StringBuilder builder = new StringBuilder();
		final CharBuffer buffer = CharBuffer.allocate(DEFAULT_BUFFER_SIZE);
		while (-1 != reader.read(buffer)) {
			builder.append(buffer.flip().toString());
		}
		return builder.toString();
	}

	/**
	 * 从FileChannel中读取内容
	 * @param fileChannel 文件管道
	 * @param charset 字符集
	 * @return 内容
	 * @throws IOException
	 */
	public static String read(FileChannel fileChannel, String charset) throws IOException {
		final MappedByteBuffer buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size()).load();
		return StringUtils.str(buffer, charset);
	}
	
	public static <T extends Collection<String>> T readLines(InputStream in, String charset, T collection) throws IOException{
		BufferedReader reader = getReader(in, charset);
		String s = null;
		while ((s = reader.readLine()) != null) {
			collection.add(s);
		}
		return collection;
	}
}
